package com.musichord.core.event;

import java.util.List;
import java.util.Vector;

import javax.sound.midi.MidiEvent;

import com.musichord.core.unit.chord.Chord;
import com.musichord.core.unit.chord.PitchPattern;
import com.musichord.core.unit.chord.TexturePattern;
import com.musichord.core.unit.chord.TexturePattern.TextureNote;

public class ChordEvent implements MCEvent {
	private Chord chord;
	private long tick = 0;
	private List<MidiEvent> midiEvents = null;

	public ChordEvent(Chord chord) {
		this.chord = chord;
	}

	public ChordEvent(Chord chord, long tick) {
		this.chord = chord;
		this.tick=tick;
	}

	public ChordEvent(int root, int duration, PitchPattern pitchPattern,
			TexturePattern texturePattern) {
		this(new Chord(root, duration, pitchPattern, texturePattern));
	}

	public List<MidiEvent> getMidiEvents0() {
		Vector<NoteEvent> noteEvents = new Vector<NoteEvent>();

		/* tick to stop */
		long t_tick = tick + chord.getDuration();

		/* number of pitches in texture */
		int[] pitches = chord.getPitchPattern().getPitches();

		/* number of pitches in pitch pattern */
		int pitchNum = pitches.length;

		TexturePattern texturePattern = chord.getTexturePattern();
		int texturePatternDuration = texturePattern.getDuration();

		/* When can add the whole loop of texture */
		while (t_tick >= tick + texturePatternDuration) {
			for (int i = 0; i < texturePattern.getPattern().size(); i++) {
				for (TextureNote textureNote : texturePattern.getPattern().get(
						i)) {

					int textureNoteDuration = textureNote.getDuration();
					int pitchNo = textureNote.getPitchNo();

					if (pitchNo < pitchNum) {
						noteEvents.add(new NoteEvent(chord.getRoot()
								+ pitches[pitchNo] + chord.getStandardPitch(),
								textureNote.getVelocity(), textureNoteDuration,
								tick + textureNote.getRelativeTick()));
					} else {
						int new_pitch = pitches[pitchNo % pitchNum]
								+ chord.getStandardPitch() + 12;
						noteEvents.add(new NoteEvent(chord.getRoot()
								+ new_pitch, textureNote.getVelocity(),
								textureNoteDuration, tick
										+ textureNote.getRelativeTick()));
					}
				}
			}
			tick += texturePatternDuration;
		}

		/* For the remaining part */
		if (tick < t_tick) {
			for (int i = 0; i < texturePattern.getPattern().size(); i++) {
				for (TextureNote textureNote : texturePattern.getPattern().get(
						i)) {

					int textureNoteDuration = textureNote.getDuration();
					int pitchNo = textureNote.getPitchNo();
					long relativeTick = textureNote.getRelativeTick();

					if (tick + relativeTick + textureNoteDuration > t_tick) {
						textureNoteDuration = (int) (t_tick - relativeTick - tick);
					}
					if (textureNoteDuration > 0) {
						if (pitchNo < pitchNum) {
							noteEvents.add(new NoteEvent(chord.getRoot()
									+ pitches[pitchNo]
									+ chord.getStandardPitch(), textureNote
									.getVelocity(), textureNoteDuration, tick
									+ relativeTick));
						} else {
							int new_pitch = pitches[pitchNo % pitchNum]
									+ chord.getStandardPitch() + 12;
							noteEvents.add(new NoteEvent(chord.getRoot()
									+ new_pitch, textureNote.getVelocity(),
									textureNoteDuration, tick + relativeTick));
						}
					}
				}
			}
		}

		List<MidiEvent> resultList = new Vector<MidiEvent>();

		for (NoteEvent noteEvent : noteEvents) {
			for (MidiEvent midiEvent : noteEvent.getMidiEvents()) {
				resultList.add(midiEvent);
			}
		}

		return resultList;
	}

	@Override
	public List<MidiEvent> getMidiEvents() {
		if (this.midiEvents != null) {
			return this.midiEvents;
		}
		this.midiEvents = this.getMidiEvents0();
		return this.midiEvents;
	}

	@Override
	public int getDuration() {
		return this.chord.getDuration();
	}

}
